#include "Task.hpp"

#include <iostream>
#include <string>
#include <vector>
#include <ctime>
#include <cassert>

#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

const int ProcessNum = 10;
std::vector<task_t> tasks;

// 先描述
class channel
{
public:
    channel(int cmdfd, pid_t slaverid, const std::string &name)
        :_cmdfd(cmdfd)
        ,_slaverid(slaverid)
        ,_name(name)
    {}
public:
    int _cmdfd;         // 发送任务的文件描述符
    pid_t _slaverid;    // 子进程的PID
    std::string _name;  // 子进程的名字
};
////////////////////////
void InitProcessPool(std::vector<channel> *channles);
void Debug(const std::vector<channel> &channels);
void slaver();
void CtrlSalver(const std::vector<channel> &channels);
void QuitProcess(const std::vector<channel> &channels);
///////////////////////
int main()
{
    LoadTask(&tasks);
    srand(time(nullptr)^getpid()^1023);  //种一个随机数种子

    // 再组织
    std::vector<channel> channels;
    // 1. 初始化
    InitProcessPool(&channels);
    Debug(channels);

    std::cout << ProcessNum << "个子进程已创建完毕，开始控制子进程执行任务\n" 
    << "####################################" << std::endl; 
    sleep(1);
    // 2. 控制子进程
    CtrlSalver(channels);
    
    std::cout << "子进程执行任务完毕，开始清理收尾\n" 
    << "####################################" << std::endl; 
    sleep(1);
    // 3. 清理收尾
    QuitProcess(channels);
    return 0;
}


/////////////////////////
void InitProcessPool(std::vector<channel> *channels)
{
     std::vector<int> oldfds;  // 确保每个子进程只有一个写端
    for (int i = 0; i < ProcessNum; ++i)
    {
        int pipefd[2];
        int n = pipe(pipefd);
        assert(!n);
        (void)n;

        pid_t id = fork();
        if (id == 0) {  // child
            std::cout << "child: " << getpid() << std::endl;
            close(pipefd[1]);  // 关闭写端
            for (auto& fd : oldfds) {   // 关闭前面父进程对应的无关子进程的写端
                close(fd);
            }
            dup2(pipefd[0], 0);
            close(pipefd[0]);  // 关闭多余读端，可注释掉，无影响
            slaver();

            close(0);
            exit(0);
        }

        // father
        close(pipefd[0]);  // 关闭读端

        // 添加channel字段
        std::string name = "Process- " + std::to_string(i);
        // 记录子进程信息
        channels->push_back(channel(pipefd[1], id, name));

        oldfds.push_back(pipefd[1]);  // 记录本次添加的写端，以下个子进程关闭该次写端，确保子进程只有一个写端

        sleep(1);
    }
}
/////////////////////////
void Debug(const std::vector<channel> &channels)
{
    for (const auto &c : channels)
    {
        std::cout << c._cmdfd << " " << c._slaverid << " " << c._name << std::endl;
    }
}
/////////////////////////
void slaver()
{
    while(true) 
    {
        int cmdcode = 0;
        //int n = read(pipefd[0], &cmdcode, sizeof(int));
        int n = read(0, &cmdcode, sizeof(int));
        if (n == sizeof(int)) {
            std::cout << "slaver say@ get a command: " << getpid() << " : comdcode: " 
            << cmdcode << std::endl;
            if (cmdcode >= 0 && cmdcode < tasks.size())  tasks[cmdcode]();
        }
        if (n == 0) break;
    }
}
/////////////////////////
void CtrlSalver(const std::vector<channel> &channels)
{
    int which = 0;
    int cnt = 8;
    while (cnt--) 
    {
        // 1. 选择任务
        int cmdcode = rand()%tasks.size();

        // 2. 选择进程
        int processpos = rand()%channels.size();

        std::cout << "father say: " << "cmdcode: " << cmdcode 
        << "already send to " << channels[processpos]._slaverid
        <<" process name: " << channels[processpos]._name << std::endl;

        // 3. 发送任务
        write(channels[processpos]._cmdfd, &cmdcode, sizeof(cmdcode));

        sleep(1);
    }
}
/////////////////////////
void QuitProcess(const std::vector<channel> &channels)
{
    for (const auto &c : channels)
    {
        close(c._cmdfd);
        waitpid(c._slaverid, nullptr, 0);
    }
}
